<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    .box{width: 200px;height:40px;line-height:40px;margin: 10px auto;}
    .box span{display: block;height: 40px;border: solid 1px black;}
    .box .list{margin: 0;padding: 0;list-style: none;border: solid 1px black;border-top: none;display: none;}
    .box .list li{padding-left: 10px;}
    .box .list li.active{background: red;color: #fff;}
    .box .list li.isYou::after{content: "√";float: right;margin-right: 10px;}

  </style>
</head>
<body>
  <select name="" id="">
    <option value="">1</option>
    <option value="">2</option>
    <option value="" selected>3</option>
    <option value="">4</option>
  </select>

  <input type="button" class="up" value="上">
  <input type="button" class="down" value="下">
  <input type="button" class="enter" value="回车">

  <div class="box">
    <span></span>
    <ul class="list">
      <li>html</li>
      <li>css</li>
      <li>javascript</li>
      <li>nodejs</li>
      <li selected>mongo</li>
      <li>java</li>
      <li>python</li>
    </ul>
  </div>
</body>
<script>
  var box = document.querySelector(".box");
  var list = document.querySelector(".list");
  var span = document.querySelector(".box span");
  var li = document.querySelectorAll(".list li");

  // 记录下拉菜单的显示或隐藏
  var state = 0;

  // 获取默认索引
  var index = getIndex();
  // 根据默认索引设置内容
  span.innerText = li[index].innerText;
  // 根据默认索引设置选中效果
  li[index].classList.add("isYou");


  // 点击显示或隐藏
  box.onclick = function(){
    if(state === 0){
      list.style.display = "block";
      state = 1;
      // 如果之前有键盘操作，初始化键盘索引和对应高亮样式
      if(keyIndex !== undefined){
        li[keyIndex].classList.remove("active");
        keyIndex = undefined;
      }
    }else{
      list.style.display = "none";
      state = 0;
    }
  }

  // 遍历所有li
  for(var i=0;i<li.length;i++){
    // 提前给li绑定索引
    li[i].index = i;
    // 鼠标进入，显示高亮
    li[i].onmouseover = function(){
      // 如果过有键盘设置的高亮
      if(keyIndex !== undefined){
        // 需要先取消
        li[keyIndex].classList.remove("active")
      }
      this.classList.add("active");
      // 将鼠标进入元素的索引，设置给键盘索引（键盘可以在鼠标进入元素的基础上继续移动）
      keyIndex = this.index;
    }
    // 鼠标离开，移除高亮
    li[i].onmouseout = function(){
      // 如果过有键盘设置的高亮
      if(keyIndex !== undefined){
        // 鼠标离开元素时也应该先取消
        li[keyIndex].classList.remove("active")
      }
      this.classList.remove("active")
      // 因为鼠标离开在清除高亮，索引此时按下键盘应该是初始效果
      keyIndex = undefined;
    }
    // 鼠标点击
    li[i].onclick = function(){
      // 取消上一次的选中效果
      li[index].classList.remove("isYou");
      // 设置内容
      span.innerText = this.innerText;
      // 添加选中效果
      this.classList.add("isYou");
      // 将本次点击的索引设置为下次要清除选中效果的索引
      index = this.index;
    }
  }

  // =========

  // 模拟键盘上键
  var up = document.querySelector(".up");
  // 模拟键盘下键
  var down = document.querySelector(".down");
  // 模拟键盘回车键
  var enter = document.querySelector(".enter");

  // 设置键盘控制高亮索引
  var keyIndex;
  // 绑定上键事件
  up.onclick = function(){
    // 下拉菜单显示状态下，键盘事件才有效
    if(state === 0) return;
    if(keyIndex === undefined){
      // 第一次按 上键
      keyIndex = li.length-1;
    }else if(keyIndex === 0){
      // 上键到边界值：
      keyIndex = 0;
    }else{
      // 不是第一次按 上键：keyIndex--
      keyIndex--;
    }
    // 取消上一个索引对应元素的高亮样式
    if(keyIndex !== li.length-1){
      li[keyIndex+1].classList.remove("active")
    }
    // 给当前索引对应元素添加高亮样式
    li[keyIndex].classList.add("active")
  }
  down.onclick = function(){
    if(state === 0) return;
    if(keyIndex === undefined){
      keyIndex = 0;
    }else if(keyIndex === li.length-1){
      keyIndex = li.length-1;
    }else{
      keyIndex++;
    }
    if(keyIndex !== 0){
      li[keyIndex-1].classList.remove("active");
    }
    li[keyIndex].classList.add("active");
  }

  enter.onclick = function(){
    // 下拉菜单显示状态下，键盘事件才有效
    if(state === 0) return;
    // 将当前高亮索引对应元素的内容设置到显示区域
    span.innerText = li[keyIndex].innerText;
    // 移除上次的选中效果
    li[index].classList.remove("isYou");
    // 给当前元素添加选中效果
    li[keyIndex].classList.add("isYou");
    // 将本次索引设置为下次要清除选中效果的索引
    index = keyIndex;
    // 隐藏下拉菜单
    list.style.display = "none";
    // 同步下拉菜单的显示或隐藏状态
    state = 0;
  }


  // =========


  // 获取默认选中的元素的索引
  function getIndex(){
    // 遍历每个li
    for(var i=0;i<li.length;i++){
      // 获取li的所有属性
      var attr = li[i].attributes;
      // 用来记录是否查找到拥有selected的元素
      var flag = false;
      // 遍历li的所有属性
      for(var j=0;j<attr.length;j++){
        // 判断是否有属性名为selected
        if(attr[j].nodeName === "selected"){
          // 修改记录
          flag = true;
        }
      }
      // 判断是否找到有selected的元素
      if(flag){
        // 有，结束循环
        break;
      }
    }
    // 返回有selected属性的元素的索引
    return i;
  }

</script>
</html>